﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using System;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using YYTPublish.App.Zs;

namespace WxShApply
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                HttpClient client = new HttpClient(new HttpHandler("1390619302", "5E3C4DAA3355FA4AD3F66DC210A5194A971FEE9E"));
                var response = client.GetAsync("https://api.mch.weixin.qq.com/v3/certificates").Result;
            }
            catch (Exception ex)
            {

                throw;
            }

        }
    }


    public class HttpHandler : DelegatingHandler
    {
        private readonly string merchantId;
        private readonly string serialNo;

        public HttpHandler(string merchantId, string merchantSerialNo)
        {
            InnerHandler = new HttpClientHandler();

            this.merchantId = merchantId;
            this.serialNo = merchantSerialNo;
        }

        protected async override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            var auth = await BuildAuthAsync(request);
            string value = $"WECHATPAY2-SHA256-RSA2048 {auth}";
            request.Headers.Add("Authorization", value);


            HttpHelper http = new HttpHelper();
            HttpItem item = new HttpItem()
            {
                URL = "https://api.mch.weixin.qq.com/v3/certificates",
                Header = new System.Net.WebHeaderCollection
                {
                    { "Authorization", value },
                    { "Accept", "application/json" }
                }
            };

            var result = http.GetHtml(item);

            var obj = JsonConvert.DeserializeObject<JObject>(result.Html);

            // 返回的证书json是一个数组
            JArray data = (JArray)obj["data"];

            // 解密证书所需要的数据 随机字符串 解密类型
            string nonce = data[0]["encrypt_certificate"]["nonce"].ToString();
            string associated_data = data[0]["encrypt_certificate"]["associated_data"].ToString();

            // 密文
            string ciphertext = data[0]["encrypt_certificate"]["ciphertext"].ToString();

            Decrypt(ciphertext, "MhugGajDz5y2WJrmyG1BCR8Us3KrBmDq", nonce);


            return await base.SendAsync(request, cancellationToken);
        }

        /// <summary>
        /// 敏感信息加密需要从https://api.mch.weixin.qq.com/risk/getcertficates此接口下载加密证书进行下一步加密，
        /// 该接口下载到的是密文，使用此AESGCM.Decrypt()方法解密得到证书明文
        /// </summary>
        /// <param name="ciphertext">接口下载得到的JSON里的ciphertext字段</param>
        /// <param name="key">微信支付商户后台设置的V3密钥</param>
        /// <param name="ivs">接口下载得到的JSON里的nonce字段</param>
        /// <param name="associatedText">默认为certificate，不需更改</param>
        /// <returns> 返回公钥明文，-----BEGIN CERTIFICATE----- </returns>
        public static string Decrypt(string ciphertext, string key, string ivs, string associatedText = "certificate")
        {
            var buff = Convert.FromBase64String(ciphertext);
            var secret = Encoding.UTF8.GetBytes(key);
            var nonce = Encoding.UTF8.GetBytes(ivs);
            var associatedData = Encoding.UTF8.GetBytes(associatedText);

            // 算法 AEAD_AES_256_GCM，C# 环境使用 BouncyCastle.Crypto.dll 类库实现
            var cipher = new GcmBlockCipher(new AesFastEngine());
            var aead = new AeadParameters(new KeyParameter(secret), 128, nonce, associatedData);
            cipher.Init(false, aead);

            var data = new byte[cipher.GetOutputSize(buff.Length)];
            var num = cipher.ProcessBytes(buff, 0, buff.Length, data, 0);
            try
            {
                cipher.DoFinal(data, num);
            }
            catch (Exception ex)
            {
            }

            return Encoding.UTF8.GetString(data);
        }

        protected async Task<string> BuildAuthAsync(HttpRequestMessage request)
        {
            string method = request.Method.ToString();
            string body = "";
            if (method == "POST" || method == "PUT" || method == "PATCH")
            {
                var content = request.Content;
                body = await content.ReadAsStringAsync();
            }

            string uri = request.RequestUri.PathAndQuery;
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            string nonce = Path.GetRandomFileName();

            string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
            string signature = Sign(message);
            return $"mchid=\"{merchantId}\",nonce_str=\"{nonce}\",timestamp=\"{timestamp}\",serial_no=\"{serialNo}\",signature=\"{signature}\"";
        }

        protected string Sign(string message)
        {
            // NOTE： 私钥不包括私钥文件起始的-----BEGIN PRIVATE KEY-----
            //        亦不包括结尾的-----END PRIVATE KEY-----
            //            string privateKey = @"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDnUCvoMHwpdiWb
            //meYmC5+RG/G7wwHeKMDRwhq3piv5fjqVo72zlBz+3mhppkByoyo4FR2pqoosBpYj
            //B/IDNygihjk74h2IsKiAavZjNvA2qUmkwyM3mAhHlf/zTBucRa3mZqm1rAaHrn1k
            //BaQeB4R12SQ0MDkl6a7mQqYkT9QwWbATxJjfw2FA74QqrL44spTCISEbXSogUQdY
            //AGeSWFdgU40+vEIRQqCLYRncxYdTLdb9mt41eKx0wLxgnkGujbN4MW/metF3V+W4
            ///16ZvCidNq/etaaAji0OVAfOX4iVB5Fm9HY4F3S3qz/2DgXKPY9DcOjFZKtBg43U
            //s9Fdh6cDAgMBAAECggEBAMlNQgi9OTCz+yKhGmN9tg4IraTjnpqCCNwC+lFcseRc
            //cAuOBidj/J/pWiCnaGrbiIBqD/h5y+loifxOi9U0iR8ShHiS3XT5mIq5i1bolsBb
            //LxXOJLXa/0mdRrOrRlQHZ8GdEpBh9wTZW5JSPnNLw4DzycgJN5LA/U6yLfhn6Jdx
            //rj1YMp8M4Y7bajs6dAFl4OKoCbPbpq1onroUcSYC/iwrF6ZPn5ANi8DBs0OJBKxs
            //yXICL5QzcdEwuOHa9nRpggfYUXUMfqwcYJ2ZlVKK0qt+uVjgTVIJh1OodbDCAwmU
            //yitQ95ezW+y3LjQkVNdx6rCEyJaBapG0PHkXJpCozEECgYEA+3abURDT3Ikkvbhz
            //aPN5dlHNzavShmMXW3pScFcZL0hlIbIFeHuIocLvMHIM4u0B/R2PQ2JT1DjbRxYa
            ///MRJX5UODEEJaEpIvzfIMrOL2xxsi7qsC8Y+QU6qW5QgGQXciQwLVOWtZEP3uZ/d
            //VMqci7UM+hC68irseHM+sw/BKBMCgYEA63yAJu24Z8AAdA95B3CJxJWl7DDGzT6Y
            //8dkoqMT/68xNc9jhBngR+MIL970hOXdkTLc1zIwV6BySopZA1laFnbxdVKFitSWB
            //Nc+RF0mFCDv96IjcZRQ//UjcXENVflHGRAsbrRenqGgyVxNFwZkkmYH8guKLgFIt
            //MqPc4JP/Q1ECgYA8VCFBGqNpdnGdHqJ5nGgYjdmtLTrNyPoylS8wCoVdRaYVpmsP
            //k7uc63KNEd+rlabzfeMADJgGAzoAXPpOiBz+VHlRUU0bey7myVA7cb3s7G3SPA/I
            //p2AKNQBEBk6JElHtgzk4vf2SX4gQPZ1RGWbjUaVyXQ8syEWBREzXksEJhwKBgB+m
            //E/Mnb+lBvM3cZn/BeEKbejlMNOvQxe17KiPElkCyrJYtR9D+wiuUqNca7CQ1D53f
            //4bMkAK25aVvApeYoqryQXFrLdeCysbLxuqZOkzqQFYfzHOvY1ODNshAUXKEljGgI
            //tieFTlsr5dgeFvn2Ax0+gxl0KRuN79UmgMEOIviBAoGAQupWvM+EzTqrpu7EkaT2
            //txCWQOB/bR6pcBcs4wGS//v1vwrO05uFtJwaVMy8jeSvIPSPOSxv1cGQVavY5b8f
            //lLR60qAbU4fP4Y6ph72+joSV9GeMvGHBtl6oumPCm39tEBbVNd4jfilhqrvvj+g+
            //1MkzVImsxQg5xT3mijD7FXM=";

            var privateKey = File.ReadAllText(@"C:\cert\service\apiclient_key.pem");

            // byte[] keyData = Convert.FromBase64String(privateKey);



            //var rsa = RSA.Create();

            //rsa.ImportParameters(ImportPrivateKey(privateKey));


            var provider = GetPrivateKey(); // <-------- call to provided ImportPrivateKey function

            //  var signingCredentials = new SigningCredentials(new RsaSecurityKey(provider), Microsoft.IdentityModel.Tokens.SecurityAlgorithms.RsaSha256);

            byte[] data = System.Text.Encoding.UTF8.GetBytes(message);
            return Convert.ToBase64String(provider.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
        }



        /// <summary>
        /// 获取私钥
        /// </summary>
        /// <returns>RSACryptoServiceProvider.</returns>
        /// <remarks>冯非凡 2021-12-30</remarks>
        public static RSACryptoServiceProvider GetPrivateKey()
        {
            var pc = new X509Certificate2(@"C:\cert\service\apiclient_cert.p12", "1390619302", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
            var rsaParam = pc.GetRSAPrivateKey().ExportParameters(true);
            var rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(rsaParam);
            return rsa;
        }

        /// <summary>
        /// Import OpenSSH PEM private key string into MS RSACryptoServiceProvider
        /// </summary>
        /// <param name="pem"></param>
        /// <returns></returns>
        public static RSACryptoServiceProvider ImportPrivateKey(string pem)
        {
            PemReader pr = new PemReader(new StringReader(pem));
            AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
            RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);

            RSACryptoServiceProvider csp = new RSACryptoServiceProvider();// cspParams);
            csp.ImportParameters(rsaParams);
            return csp;
        }


        //public static RSAParameters ImportPrivateKey(string pem)
        //{
        //    PemReader pr = new PemReader(new StringReader(pem));
        //    RsaPrivateCrtKeyParameters privKey = (RsaPrivateCrtKeyParameters)pr.ReadObject();
        //    RSAParameters rp = new RSAParameters();
        //    rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
        //    rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
        //    rp.P = privKey.P.ToByteArrayUnsigned();
        //    rp.Q = privKey.Q.ToByteArrayUnsigned();
        //    rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
        //    rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
        //    rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
        //    rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);


        //    return rp;
        //}

        private static byte[] ConvertRSAParametersField(BigInteger n, int size)
        {
            byte[] bs = n.ToByteArrayUnsigned();
            if (bs.Length == size)
                return bs;
            if (bs.Length > size)
                throw new ArgumentException("Specified size too small", "size");
            byte[] padded = new byte[size];
            Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
            return padded;
        }
    }
}
